export function FileLineReader(file, callback, fetchSize = 8 * 1024) {
    this.file = file;
    this.bufferedLines = [];
    this.bufferedtexts = "";
    this.pos = 0;
    this.callback = callback;
    this.fetchSize = fetchSize;
}

function indexOfLineBreaker(content, startPos) {
    for (let i = startPos; i < content.length; ++i) {
        if (content.charAt(i) === '\n') {
            return {pos: i, len: 1};
        }
        if (content.charAt(i) === '\r') {
            if (content.charAt(i + 1) === '\n') {
                return {pos: i, len: 2};
            } else {
                return {pos: i, len: 1};
            }
        }
    }
    return {pos: -1};
}

FileLineReader.prototype.pushToBuffer = function (content) {
    let startPos = 0;
    while (startPos < content.length) {
        let breakPos = indexOfLineBreaker(content, startPos);
        if (breakPos.pos < 0) {
            this.bufferedtexts += content.substring(startPos);
            startPos = content.length;
        } else {
            this.bufferedLines.push(this.bufferedtexts + content.substring(startPos, breakPos.pos));
            this.bufferedtexts = "";
            startPos = breakPos.pos + breakPos.len;
        }
    }
    if (this.pos >= this.file.size) {
        this.bufferedLines.push(this.bufferedtexts);
        this.bufferedtexts = "";
    }
}

FileLineReader.prototype.getProcessing = function() {
    // pos代表从文件读了多少，并不代表真正处理了多少，实际大部分时间应该耗费在正则表达式等匹配上
    // 但是要精确计算出当前处理了多少有些麻烦，所以先做个不那么精确的，以后有需要再优化
    return Math.min(this.pos, this.file.size);
}

// 基于事件循环的代码挺难写，同时研究过FileReaderSync + WebWorker的可行性，
// 发现WebWorker与主线程之间传递数据有结构化克隆和可转移对象两种，但是不论是哪种方式，
// 可能都没法正确将ProfilingData的复杂结构传递到主线程。所以只能捏着鼻子基于事件循环来写了
FileLineReader.prototype.read = function () {
    while (this.bufferedLines.length > 0) {
        this.callback(this.bufferedLines.shift(), this.getProcessing());
    }

    if (this.pos < this.file.size) {
        let slicedEnd = this.pos + this.fetchSize;
        let slicedFile = this.file.slice(this.pos, slicedEnd);
        this.pos = slicedEnd;
        let flr = this;
        let fr = new FileReader();
        fr.onload = function() {
            flr.pushToBuffer(fr.result);
            flr.read();
        }
        fr.onerror = function () {
            flr.callback(null);
        }
        fr.readAsText(slicedFile, 'UTF-8');
    } else {
        this.callback(null, this.getProcessing());
    }
}
